/*
 * @Description: 珠峰js面试题  闭包栈堆，深浅栈堆
 * @Author: BaldHead
 * @Date: 2022-03-20 09:27:34
 * @LastEditTime: 2022-03-22 14:02:23
 * @LastEditors: Please set LastEditors
 */
//example 1
var a = {},
    b = '0',
    c = 0;
a[b] = "小红";
a[c] = "小明"
    // console.log(a[b]); //小明
    //example 2
var a = {},
    b = Symbol('1'),
    c = Symbol('1');
a[b] = "小红";
a[c] = "小明"
    // console.log(a[b]); //小红

//example3
var a = {},
    b = { n: '1' },
    c = { m: '2' };
a[b] = "小红";
a[c] = "小明";
// console.log(a[b]); //小红
/**
 * @description:闭包  -保存作用 作用域
 * @version: 
 * @param  {*}
 * @return {*}
 * @Author: BaldHead
 * @Date: 2022-03-20 09:47:32
 * @LastEditTime: Do not edit
 * @LastEditors:  
 */
var test = (function(i) {
    return function() {
        alert(i *= 2);
    }
})(2);
// test(5); //'4'
var a = 0,
    b = 0;

function A(a) {
    A = function(b) {
        alert(a + b++);
    }
    alert(a++);
}
// A(1); //‘1’
// A(2); //‘4’
/**
 * @description: 对象(数组)的深克隆和浅克隆 头条
 * @version: 
 * @param  {*}
 * @return {*}
 * @Author: BaldHead
 * @Date: 2022-03-20 09:55:27
 * @LastEditTime: Do not edit
 * @LastEditors:  
 */
let obj = {
        a: 100,
        b: [10, 20, 30],
        c: {
            x: 10
        },
        d: /^d+$/,
        e: function() {
            console.log(1);
        },
        f: new Date(),
        g: Symbol('1'),
        h: undefined,
        i: null
    }
    //浅克隆 -es6 扩展运算符
let obj1 = {...obj };
// 浅克隆 -循环遍历 
let obj2 = {};
for (let key in obj) {
    if (!obj.hasOwnProperty(key)) break; //不是私有属性不赋值, hasOwnPrototy() 判断是否有指定属性
    obj2[key] = obj[key];
}
// console.log(obj === obj2);
// obj.c.x = 1000;
// console.log(JSON.stringify(obj));
// console.log(JSON.parse(JSON.stringify(obj)));
// console.log(obj2); //obj2也变了
//{ a: 100, b: [10, 20, 30], c: { x: 1000 }, d: /^d+$/ }
// { a: 100, b: [10, 20, 30], c: { x: 1000 }, d: /^d+$/ }
//深克隆
/**
 * @description:深克隆 
 * @version: 
 * @param  {*}
 * @return {*}
 * @Author: BaldHead
 * @Date: 2022-03-20 11:54:24
 * @LastEditTime: Do not edit
 * @LastEditors:  
 */

function deepClone(obj) {
    //判断obj是什么属性的
    //不是引用对象返回 (Symbol,undefined)
    //typeof null =='Object'
    if (obj === null) return null;
    if (typeof obj != 'object') return obj;
    //Function
    if (obj instanceof Function) return new Function(obj);
    //RegExp
    if (obj instanceof RegExp) return new RegExp(obj);
    //Date
    if (obj instanceof Date) return new Date(obj);
    //创建一个新的obj
    let newObj = new obj.constructor;
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            //递归调用
            newObj[key] = deepClone(obj[key]);
        }
    }
    return newObj;
}
let deepObj = deepClone(obj);
console.log(obj);
console.log(deepObj);
// console.log(obj === deepObj);
// console.log(obj.f === deepObj.f);
console.log(undefined == null); //true
console.log(undefined === null); //false
console.log(null == null); //true
console.log(undefined == undefined); //true
console.log(null === null); //true
console.log(undefined === undefined); //true
  
/**一句话总结两者的区别就是：
undefined表示一个变量自然的、 最原始的状态值，
而 null 则表示一个变量被人为的设置为空对象， 而不是原始状态。
所以， 在实际使用过程中， 为了保证变量所代表的语义， 不要对一个变量显式的赋值 undefined，
当需要释放一个对象时， 直接赋值为 null 即可。 * /
/**
 * @description:  面向对象  变量函数提升
 * @version: 
 * @param  {*}
 * @return {*}
 * @Author: BaldHead
 * @Date: 2022-03-20 14:56:59
 * @LastEditTime: Do not edit
 * @LastEditors:  
 */
function Foo() {
    getName = function() {
        console.log(1);
    }
    return this;
}
Foo.getName = function() {
    console.log(2);
}
Foo.prototype.getName = function() {
    console.log(3);
}
var getName = function() {
    console.log(4);
}

function getName() {
    console.log(5);
}
Foo.getName(); //2
getName(); //4
Foo().getName(); //1
getName(); //1
new Foo.getName(); //2
new Foo().getName(); //3
new new Foo().getName(); //3
/**
 * @description: EventLoop
 * @version: 
 * @param  {*}
 * @return {*}
 * @Author: BaldHead
 * @Date: 2022-03-20 19:14:38
 * @LastEditTime: Do not edit
 * @LastEditors:  
 */
//变量提升，带var的提前声明,function的提前申明定义
//async1
async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
//async2
async function async2() {
    console.log('async2');

}
console.log('script start');
setTimeout(function() {
    console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
})
console.log('script end');

// 作业题
/**
 * @description: 课外练习题
 * @version: 
 * @param  {*}
 * @return {*}
 * @Author: BaldHead
 * @Date: 2022-03-21 15:13:24
 * @LastEditTime: Do not edit
 * @LastEditors:  
 */
function A() {
    // alert(1);
    console.log(1);
}

function Func() {
    A = function() {
        // alert(2);
        console.log(2);

    };
    return this;
}
Func.A = A;

Func.prototype = {
    A: () => {
        // alert(3);
        console.log(3);
    }
};
A(); //'1'
Func.A(); //'1'
Func().A(); //'2'
new Func.A(); // '1'
new Func().A(); //'3'
new new Func().A(); // (intermediate value).A is not a constructor
//箭头函数不饿能被new
//箭头函数和普通函数的 区别
// 箭头函数没有自己的主体(没有this), this的指向继承上下问
//普通函数有主题
//构造函数没有原型链的，没有constructor
/**
 * @description: 
 * @version: 
 * @param  {*}
 * @return {*}
 * @Author: BaldHead
 * @Date: 2022-03-21 15:40:12
 * @LastEditTime: Do not edit
 * @LastEditors:  
 */
var x = 2;
var y = {
    x: 3,
    z: (function(x) {
        this.x *= x;
        x += 2;
        return function(n) {
            this.x *= n;
            x += 3;
            console.log(x);
        }
    })(x)
};
var m = y.z;
m(4);
y.z(5)
console.log(x, y.x);
/**
 * @description: 
 * @version: 
 * @param  {*}
 * @return {*}
 * @Author: BaldHead
 * @Date: 2022-03-21 15:56:49
 * @LastEditTime: Do not edit
 * @LastEditors:  
 */
//两个等号 对象==字符串  对象.toString()变为字符串
//null==undefined 和其他值比较不相等
//NaN==NaN false
//剩下的转换为数字

//1。重写oString()  y引申 valueOf和toString的区别    
//分支 object.toString=Array.shift   Array转换成字符串 oString valueof  join
var a = {
    i: 0,
    toString() {
        return ++this.i;
    }
}

var a = [1, 2, 3];
a.toString = a.shift;
//2.数据劫持  definedProperty() 监听get和set函数   只能是var，放在了全局上   放Index.html
var i = 0;
Object.defineProperty(window, 'a', {
    set() {

    },
    get() {
        return ++i;
    }
});
// var a; //a等于什么值
if (a == 1 && a == 2 && a == 3) {
    console.log(1);
}
/**
 * @description: 
 * @version: 
 * @param  {*}
 * @return {*}
 * @Author: BaldHead
 * @Date: 2022-03-21 15:59:44
 * @LastEditTime: Do not edit
 * @LastEditors:  
 */
var x = 0,
    y = 1;

function fn() {
    x += 2;
    fn = function(y) {
        console.log(y + (--x));
    }
    console.log(x, y);
}
fn(3); //2 1
fn(4); //5
console.log(x, y); //1, 1
/**
 * @description: 
 * @version: 
 * @param  {*}
 * @return {*}
 * @Author: BaldHeadn
 * @Date: 2022-03-21 16:01:42
 * @LastEditTime: Do not edit
 * @LastEditors:  
 */
setTimeout(() => {
    console.log(1, Date.now());
}, 20)
console.log(2, Date.now());
setTimeout(() => { console.log(3, Date.now()); }, 10)
console.log(4, Date.now());
console.time('AA');
for (let index = 0; index < 90000000; index++) {}
console.timeEnd('AA');
console.log(5, Date.now());
setTimeout(() => {
    console.log(6, Date.now());
}, 8)
console.log(7, Date.now());
setTimeout(() => {
    console.log(8, Date.now());
}, 15)
console.log(9, Date.now());